import { ConstantEnum } from '@libs/common/enums';
import {
  DingNotificationErrorEvent,
  DingNotificationGroupEvent,
  DingNotificationPersonalEvent,
} from '@libs/common/event-emitter';
import { BusinessException } from '@libs/common/exceptions';
import { LoggerService } from '@libs/common/logger/logger.service';
import { redis_buy_product_key, RedisService } from '@libs/common/redis';
import {
  BaseRepository,
  indexPaginateBuilder,
  showBuilderEntity,
} from '@libs/common/repository';
import { generateOrderNumber } from '@libs/common/utils';
import { forwardRef, Inject, Injectable } from '@nestjs/common';
import { EventEmitter2 } from '@nestjs/event-emitter';
import { Dayjs } from '@shencom/utils-date';
import { plainToInstance } from 'class-transformer';
import { Between, DataSource, Equal, In, MoreThanOrEqual } from 'typeorm';

import { DeviceCargoLaneService } from '@/atm/device-cargo-lane/device-cargo-lane.service';
import { OrderResult } from '@/constant.enum';
import { DepartmentConfigService } from '@/dingding/department-config/department-config.service';

import { OrderService } from '../order/order.service';
import { PointsService } from '../points/points.service';
import {
  BuyProductDto,
  CreateProductDto,
  DeleteProductDto,
  IndexProductDto,
  ShowProductDto,
  UpdateProductDto,
} from './dto/product.dto';
import { Product, StatusEnum } from './entities/product.entity';
import { DeviceExchangeLogService } from '@/atm/device-exchange-log/device-exchange-log.service';

@Injectable()
export class ProductService extends BaseRepository {
  constructor(
    @Inject(ConstantEnum.DB_DATA_SOURCE_MANAGER) dataSource: DataSource,
  ) {
    super(dataSource);
  }

  protected repositoryManager = () => this.getRepository(Product);

  protected repository = this.getRepository(Product);

  @Inject(EventEmitter2)
  private eventEmitter: EventEmitter2;

  @Inject(LoggerService)
  private logger: LoggerService;

  @Inject(RedisService)
  private readonly redisService: RedisService;

  @Inject(PointsService)
  private readonly pointsService: PointsService;

  @Inject(DepartmentConfigService)
  private readonly departmentConfigService: DepartmentConfigService;

  @Inject(forwardRef(() => OrderService))
  private readonly orderService: OrderService;

  @Inject(forwardRef(() => DeviceCargoLaneService))
  private readonly deviceCargoLaneService: DeviceCargoLaneService;

  @Inject(DeviceExchangeLogService)
  private readonly deviceExchangeLogService: DeviceExchangeLogService;

  async index(dto: IndexProductDto) {
    const res = await indexPaginateBuilder({
      dto,
      repository: this.repository,
    });
    return res;
  }

  show<D extends ShowProductDto = ShowProductDto>(dto: D) {
    return showBuilderEntity(this.repository, dto);
  }

  async delete(dto: DeleteProductDto) {
    const { id, ids } = dto;
    if (id) {
      await this.repositoryManager().delete({ id: Equal(id) });
    } else if (ids) {
      await this.repositoryManager().delete({ id: In(ids) });
    }
    return;
  }

  async update(dto: UpdateProductDto) {
    await this.repositoryManager().update(dto.id, dto);
    return;
  }

  async create(dto: CreateProductDto | CreateProductDto[]) {
    const entity = this.repositoryManager().create(
      plainToInstance(Product, dto),
    );
    await this.repositoryManager().insert(entity);
    return entity;
  }

  switch(id: string, status: StatusEnum) {
    return this.repositoryManager().update({ id }, { status });
  }

  findOne(dto: { id?: string }) {
    if (dto.id) {
      return this.repository.findOneBy({ id: dto.id });
    }
    return null;
  }

  find(dto: { id?: string }) {
    if (dto.id) {
      return this.repository.findOneBy({ id: dto.id });
    }
    return null;
  }

  stock(id: string, stock: number) {
    if (stock < 0) {
      throw new Error('库存不能小于0');
    }

    return this.repositoryManager().update({ id }, { stock });
  }

  async buy(dto: BuyProductDto, user: SC.User.SysInfo, mcuid: string) {
    try {
      if (!dto.productId) throw new Error('商品id不能为空');

      dto.number = 1; // 固定只能购买一个商品

      // 加锁
      const lock = await this.redisService.get(redis_buy_product_key(mcuid));

      if (lock) {
        throw new Error('设备忙，请稍后再试');
      }

      const cargoLane = await this.deviceCargoLaneService.findOne(mcuid, {
        productId: dto.productId,
        store: MoreThanOrEqual(dto.number),
      });

      if (!cargoLane) throw new Error('物品不存在');

      if (!cargoLane.product) throw new Error('商品不存在');

      // 然后判断是否充足
      if (cargoLane.store! < dto.number) throw new Error('商品不足');

      // 查询用户的积分是否充足, 如果积分不充足, 则返回错误信息
      const { points } =
        (await this.pointsService.findUserPoints(user.id)) || {};
      if (!points) throw new Error('您的积分不足');

      const { discountPrice, originalPrice } = cargoLane.product;

      const price = discountPrice || originalPrice || 0;

      if (price <= 0) {
        throw new Error('商品价格异常: ' + price);
      }

      // 计算商品的总价
      const totalPrice = price * dto.number;

      if (points < totalPrice) throw new Error('您的积分不足');

      // 判断当前商品是否限量，0 为不限量
      if ((cargoLane.product.maxBuy || 0) > 0) {
        // 获取今日购买的记录
        const todayOrder = await this.orderService.find({
          createdAt: Between(
            Dayjs().startOf('day').valueOf(),
            Dayjs().endOf('day').valueOf(),
          ),
          productId: dto.productId,
          userId: user.id,
          status: 1,
        });
        // 计算已经购买的数量
        const todayOrderNumber = todayOrder.reduce(
          (a, b) => a + b.productNumber,
          0,
        );
        // 如果当前商品限量的数量
        if (todayOrderNumber >= dto.number) {
          throw new Error('今日购买数量已达到上限');
        }
      }

      const departmentConfig =
        await this.departmentConfigService.userIdFindFirstConfig(user.uid);

      console.log('部门配置信息 :>> ', departmentConfig);

      // 最大限制积分
      if (departmentConfig) {
        // 每天消费根据部门限制
        // 获取今天的消费积分
        const day_start = Dayjs().startOf('day').valueOf();
        const day_end = Dayjs().endOf('day').valueOf();
        const userOrder = await this.orderService.find({
          createdAt: Between(day_start, day_end),
          userId: user.uid,
          status: 1,
        });

        const userTotalPoints = userOrder.reduce((pre, cur) => {
          return (pre += cur.totalPrice);
        }, totalPrice);

        console.log('今日消费积分 :>> ', userTotalPoints);

        const maxPoints = departmentConfig.dayMaxPoints;

        if (userTotalPoints >= maxPoints) {
          throw new Error('今日消费积分已达上限');
        }
      }

      const orderNo = generateOrderNumber();
      const transNo = generateOrderNumber();

      const productVo = {
        productId: cargoLane.product.id,
        productName: cargoLane.product.name,
        productPrice: price,
        productNumber: dto.number,
        productImage: cargoLane.product.image,
      };

      const deviceVo = {
        deviceTransNo: transNo,
        deviceRoadId: cargoLane.deviceRoad.id,
        deviceRoadNo: cargoLane.deviceRoad.roadNo,
        deviceResult: OrderResult['待出货'],
      };

      // 创建订单
      const order = await this.orderService.createOrder({
        orderNo,
        totalPrice,
        userName: user.realname!,
        userId: user.id,
        status: deviceVo.deviceResult,
        ...deviceVo,
        ...productVo,
      });

      this.deviceExchangeLogService.deviceExchange({
        userId: order.userId,
        userName: order.userName,
        orderStatus: order.status,
        orderNo: order.orderNo,
        deviceResult: OrderResult['待出货'],
        deviceRoadId: order.deviceRoadId,
        deviceRoadNo: order.deviceRoadNo,
        deviceTransNo: order.deviceTransNo,
        productId: order.productId,
        productName: order.productName,
        productNumber: order.productNumber,
        productPrice: order.productPrice,
      });

      await this.redisService.set(redis_buy_product_key(mcuid), 1);

      await this.deviceCargoLaneService.delivery(mcuid, {
        id: cargoLane.id,
        productId: dto.productId,
        number: dto.number,
        transNo: transNo,
      });

      try {
        // 检查设备出货状态
        const result = await this.orderService.checkOrderStatus(mcuid, order);

        if (result?.pointsHistoryId) {
          // 个人通知
          this.eventEmitter.emit(
            DingNotificationPersonalEvent.key,
            new DingNotificationPersonalEvent({
              points: totalPrice,
              userDingId: user.dingId,
              pointsHistoryId: result.pointsHistoryId,
              ...productVo,
            }),
          );
        }

        // 群通知
        this.eventEmitter.emit(
          DingNotificationGroupEvent.key,
          new DingNotificationGroupEvent({
            orderNo: orderNo,
            userName: user.realname || '-',
            points: totalPrice,
            result: result ? OrderResult[result.status] : '异常',
            ...productVo,
          }),
        );

        await this.orderService.updateStatus(order.id, result?.status || -1);

        return orderNo;
      } catch (error) {
        this.logger.error(error);
        this.eventEmitter.emit(
          DingNotificationErrorEvent.key,
          new DingNotificationErrorEvent({
            name: '兑换异常',
            message: `### ${orderNo}\n#### 用户：${user.realname}\n#### UID： ${user.id}\n#### 商品：${productVo.productName} * ${productVo.productNumber}\n#### 总价：${totalPrice}`,
          }),
        );
        throw new BusinessException(error);
      }
    } catch (error) {
      throw new BusinessException(error);
    } finally {
      this.redisService.del(redis_buy_product_key(mcuid));
    }
  }
}
